﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace eventModel.util.xml
{
    class QueryableXml
    {
        XmlElement root;

        public XmlElement Root
        {
            get
            {
                return root;
            }
        }

        public QueryableXml(string xml)
        {
            int start = 0;
            root = XmlElement.parseNext(xml, ref start);
        }

        public override string ToString()
        {
            return root.ToString();
        }
    }

    public class XmlElement
    {
        string tagname;
        string content;
        Dictionary<string, string> attributes;
        LinkedList<XmlElement> children;

        public string TagName
        {
            get
            {
                return tagname;
            }
        }

        public string this[string attribute]
        {
            get
            {
                return attributes[attribute];
            }
            set
            {
                attributes[attribute] = value;
            }
        }

        public LinkedList<XmlElement> Children
        {
            get
            {
                return children;
            }
        }

        private XmlElement init(string TagName, Dictionary<string, string> Attributes, string Content, LinkedList<XmlElement> Children)
        {
            tagname = TagName;
            attributes = Attributes;
            content = Content;
            children = Children;

            return this;
        }

        public XmlElement()
        {
            tagname = null;
            attributes = null;
            content = null;
            children = null;
        }

        public static string Extract(string text, string begninning, string end, ref int start)
        {
            int startIndex = text.IndexOf(begninning, start);
            if (startIndex < 0)
                return "";

            startIndex += begninning.Length;

            int stopIndex = text.IndexOf(end, startIndex);
            if (stopIndex < 0)
                return "";

            string extract = text.Substring(startIndex, stopIndex - startIndex); //text.Remove(stopIndex).Substring(startIndex);
            start = stopIndex + end.Length;
            return extract;
        }

        public static XmlElement parseNext(string XML, ref int start)
        {
            string tagStart = Extract(XML, "<", ">", ref start);

            bool ended = false;
            if (tagStart.EndsWith("/"))
            {
                tagStart = tagStart.Remove(tagStart.Length - 1);
                ended = true;
            }

            int tagStartIndex = 0;
            string tagName = Extract(tagStart, "", " ", ref tagStartIndex);
            if (tagName.Length == 0)
                tagName = tagStart;

            Dictionary<string, string> attributes = new Dictionary<string, string>();

            while (tagStart.Length > tagStartIndex)
            {
                while (tagStart[tagStartIndex] == ' ')
                    tagStartIndex++; // trim beginning
                string attributeName = Extract(tagStart, "", "=", ref tagStartIndex);
                while (tagStart[tagStartIndex] == ' ')
                    tagStartIndex++; // trim beginning
                string quoteChar;
                if (tagStart[tagStartIndex] == '"')
                    quoteChar = "\"";
                else if (tagStart[tagStartIndex] == '\'')
                    quoteChar = "'";
                else
                {
                    tagStartIndex = tagStart.Length;
                    break;
                }
                string attributeValue = Extract(tagStart, quoteChar, quoteChar, ref tagStartIndex);
                attributes.Add(attributeName, attributeValue);
            }

            LinkedList<XmlElement> children = new LinkedList<XmlElement>();
            string content = null;

            if (!ended)
            {
                try
                {
                    while (XML.IndexOf("<", start) < XML.IndexOf("</" + tagName, start))
                    {
                        children.AddLast(parseNext(XML, ref start));
                    }

                    int endtagIndex = XML.IndexOf("</" + tagName, start);

                    if (children.Count == 0)
                        content = XML.Substring(start, endtagIndex - start);

                    start = endtagIndex + tagName.Length + 3;
                }
                catch (Exception innerError)
                {
                    try
                    {
                        foreach (XmlElement child in children)
                            child.Close();
                        children.Clear();
                    }
                    catch
                    { }
                    throw innerError;
                }
            }

            XmlElement xmlElement = Factory<XmlElement>.Checkout();

            return xmlElement.init(tagName, attributes, content, children);
        }

        public override string ToString()
        {
            StringBuilder xml = new StringBuilder(50000);
            buildXML(xml);
            return xml.ToString();
        }

        private void buildXML(StringBuilder XML)
        {
            XML.Append("<" + TagName);
            foreach (KeyValuePair<string, string> attribute in attributes)
            {
                char quoteChar;
                attribute.Value.Replace("\"", "&quot;");
                quoteChar = '\''; // Always use double quotes and replace quotes in value with &quot;
                XML.Append(" " + attribute.Key + "=" + quoteChar + attribute.Value + quoteChar);
            }
            if (children.Count == 0 && content == null)
                XML.Append("/>");
            else
            {
                XML.Append(">");
                if (children.Count > 0)
                {
                    foreach (XmlElement child in children)
                    {
                        child.buildXML(XML);
                    }
                }
                else if (content != null)
                    XML.Append(content);
                XML.Append("</" + TagName + ">");
            }
        }

        
        
        public void Close()
        {
            //TODO: Dave was in the middle of working on this so it does not compile yet.
            /*
            Factory<XmlElement> factory = Factory<XMLNode>.GetFactory();

            if (children != null)
            {
                foreach (XmlElement child in children)
                {
                    child.Close();
                    factory.Return(child);
                }
         
            }
            */
            tagname = null;
            content = null;
            attributes = null;
            children = null;
        }
        
    }
}